package scheduler

import (
	"errors"
	"fmt"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEntityManager"
	"gitee.com/fierce_wolf/go-fox-edge-common/commSql"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Json"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/String"
	"gitee.com/fierce_wolf/go-fox-edge-common/edgeEntity"
	"strings"
	"time"
)

type PeriodRecordScheduler struct {
	lastTimeMap       map[string]interface{}
	lastDeleteInvalid int64
}

// Scheduler 创建一个线程
func (e *PeriodRecordScheduler) Scheduler() {
	go e.scheduler()
}

// 线程函数
func (e *PeriodRecordScheduler) scheduler() {
	for true {
		time.Sleep(1 * time.Second)
		if !commEntityManager.EntityManager.Initialized {
			continue
		}

		// 删除失效的数据
		e.deleteInvalid()

		// 插入记录
		e.insertRecord()
	}
}

func (e *PeriodRecordScheduler) deleteOverload(taskId int64, maxBatch int) {
	sumCount, err := commSql.Client.QueryCount("SELECT COUNT(1) FROM  tb_period_record WHERE task_id = " + String.ToString(taskId))
	if err != nil || sumCount <= maxBatch {
		return
	}

	// 删除旧记录
	sql := fmt.Sprintf("DELETE FROM  tb_period_record  WHERE task_id = %v order BY id LIMIT  %v", taskId, sumCount-maxBatch)
	commSql.Client.Exec(sql, nil)
}

func (e *PeriodRecordScheduler) getBindDeviceIds(taskEntity *edgeEntity.PeriodTaskEntity) ([]int64, error) {
	deviceIds := make([]int64, 0)

	if taskEntity.SelectDevice {
		// 组织跟该任务相关的设备ID
		deviceIds = taskEntity.DeviceIds
	} else {
		entityList, err := commEntityManager.GetEntityMap(edgeEntity.Type.DeviceEntity.Type())
		if err != nil {
			return nil, err
		}
		for _, entity := range entityList {
			deviceEntity := entity.(*edgeEntity.DeviceEntity)
			manufacturer := deviceEntity.Manufacturer
			deviceType := deviceEntity.DeviceType

			if (manufacturer == taskEntity.Manufacturer) && deviceType == (taskEntity.DeviceType) {
				deviceIds = append(deviceIds, deviceEntity.GetBaseEntity().Id)
			}
		}
	}

	return deviceIds, nil
}

func (e *PeriodRecordScheduler) appendRecordEntity(recordBatch string, taskId int64, taskObjectIds []string, valueEntity *edgeEntity.DeviceValueEntity, periodRecordEntityList []*edgeEntity.PeriodRecordEntity) []*edgeEntity.PeriodRecordEntity {
	datetime := time.Now().UnixMilli()

	if valueEntity == nil {
		return periodRecordEntityList
	}

	for _, objectName := range taskObjectIds {
		value := valueEntity.Params[objectName]
		if value == nil {
			continue
		}

		jsn := Json.BuildJson(value)
		data := Json.BuildMapByJson(jsn)

		periodRecordEntity := edgeEntity.Type.PeriodRecordEntity.Instance()
		periodRecordEntity.TaskId = taskId
		periodRecordEntity.DeviceId = valueEntity.BaseEntity.Id
		periodRecordEntity.RecordBatch = recordBatch
		periodRecordEntity.ObjectName = objectName
		periodRecordEntity.ObjectValue = data
		periodRecordEntity.BaseEntity.CreateTime = datetime

		periodRecordEntityList = append(periodRecordEntityList, periodRecordEntity)
	}

	return periodRecordEntityList
}

func (e *PeriodRecordScheduler) deleteInvalid() {
	if time.Now().UnixMilli()-e.lastDeleteInvalid < 10*60*1000 {
		return
	}

	e.lastDeleteInvalid = time.Now().UnixMilli()

	entityList, err := commSql.Query(edgeEntity.Type.PeriodRecordEntity.Type(), "SELECT DISTINCT t.task_id  FROM tb_period_record t")
	if err != nil || len(entityList) == 0 {
		return
	}

	for _, val := range entityList {
		entity := val.(*edgeEntity.PeriodRecordEntity)

		if commEntityManager.GetEntityById(edgeEntity.Type.PeriodTaskEntity.Type(), entity.TaskId) != nil {
			continue
		}

		_, err = commSql.Client.Exec("DELETE FROM tb_period_record t WHERE t.task_id = "+String.ToString(entity.TaskId), nil)
		if err != nil {
			return
		}
	}
}

func (e *PeriodRecordScheduler) makePeriodBatch(taskEntity *edgeEntity.PeriodTaskEntity, lastTime int64) (string, error) {
	timeMode := Map.GetString(taskEntity.TaskParam, "timeMode", "")
	timeUnit := Map.GetString(taskEntity.TaskParam, "timeUnit", "")
	timeInterval := Map.GetInt64(taskEntity.TaskParam, "timeInterval", 0)

	if timeMode == "" || timeUnit == "" || timeInterval == int64(0) {
		return "", errors.New("配置参数不正确")
	}

	nowTime := time.Now()
	currentTime := nowTime.UnixMilli()
	if timeMode == "interval" {
		if timeUnit == "second" {
			if currentTime-lastTime > timeInterval*1000 {
				formatted := nowTime.Format("2006-01-02 15:04:05")
				return formatted, nil
			}
		}
		if timeUnit == "minute" {
			if currentTime-lastTime > timeInterval*1000*60 {
				formatted := nowTime.Format("2006-01-02 15:04")
				return formatted, nil
			}
		}
		if timeUnit == "hour" {
			if currentTime-lastTime > timeInterval*1000*3600 {
				formatted := nowTime.Format("2006-01-02 15")
				return formatted, nil
			}
		}
		if timeUnit == "day" {
			if currentTime-lastTime > timeInterval*1000*3600*24 {
				formatted := nowTime.Format("2006-01-02")
				return formatted, nil
			}
		}

	}

	return "", errors.New("timeUnit非法")
}

func (e *PeriodRecordScheduler) insertRecord() {
	periodRecordEntityList := make([]*edgeEntity.PeriodRecordEntity, 0)

	taskList, err := commEntityManager.GetEntityMap(edgeEntity.Type.PeriodTaskEntity.Type())
	if err != nil {
		return
	}

	for _, entity := range taskList {
		taskEntity := entity.(*edgeEntity.PeriodTaskEntity)

		// 检查：是否到了执行周期
		lastTime := Map.GetInt64(e.lastTimeMap, taskEntity.TaskName, 0)

		periodBatch, err := e.makePeriodBatch(taskEntity, lastTime)
		if err != nil || periodBatch == "" {
			continue
		}

		e.lastTimeMap[taskEntity.TaskName] = time.Now().UnixMilli()

		// 组织跟该任务相关的设备ID
		deviceIds, err := e.getBindDeviceIds(taskEntity)
		for _, deviceId := range deviceIds {
			// 获得设备对象
			deviceEntity := commEntityManager.GetEntityById(edgeEntity.Type.DeviceEntity.Type(), deviceId)
			if deviceEntity == nil {
				continue
			}

			// 获得数值对象
			valueEntity, err := commEntityManager.GetEntityByServiceKey(edgeEntity.Type.DeviceValueEntity.Type(), deviceEntity.MakeServiceKey())
			if err != nil || valueEntity == nil {
				continue
			}

			// 每超过100条记录，就保存数据
			periodRecordEntityList = e.appendRecordEntity(periodBatch, taskEntity.BaseEntity.Id, taskEntity.ObjectIds, valueEntity.(*edgeEntity.DeviceValueEntity), periodRecordEntityList)
			if len(periodRecordEntityList) > 100 {
				e.insert(periodRecordEntityList)
				periodRecordEntityList = make([]*edgeEntity.PeriodRecordEntity, 0)
			}
		}

		if len(periodRecordEntityList) > 0 {
			e.insert(periodRecordEntityList)
			periodRecordEntityList = make([]*edgeEntity.PeriodRecordEntity, 0)
		}

		// 每个任务最大10W条记录，超过的就删除
		e.deleteOverload(taskEntity.BaseEntity.Id, 10*10000)
	}
}

func (e *PeriodRecordScheduler) insert(entityList []*edgeEntity.PeriodRecordEntity) {
	pageSize := 100
	pageList := make([]*edgeEntity.PeriodRecordEntity, 0)
	for i := 0; i < len(entityList); i++ {
		pageList = append(pageList, entityList[i])

		insert := len(pageList) == pageSize
		if (len(entityList)/pageSize == i/pageSize) && (len(entityList)%pageSize-1 == i%pageSize) {
			insert = true
		}

		if !insert {
			continue
		}

		sb := strings.Builder{}
		sb.WriteString("INSERT INTO `tb_period_record` (`task_id`, `record_batch`, `device_id`, `object_name`, `object_value`, `create_time`) VALUES ")

		values := make([]interface{}, 0)

		for pageId := 0; pageId < len(pageList); pageId++ {
			entity := pageList[pageId]

			//sb.WriteString(" (" + String.ToString(entity.TaskId) + ", ")
			//sb.WriteString(" '" + entity.RecordBatch + "', ")
			//sb.WriteString(" " + String.ToString(entity.DeviceId) + ", ")
			//sb.WriteString(" '" + entity.ObjectName + "', ")
			//sb.WriteString(" '" + String.ToString(entity.ObjectValue) + "', ")
			//if pageId < len(pageList)-1 {
			//	sb.WriteString(" " + String.ToString(entity.BaseEntity.CreateTime) + "),")
			//} else {
			//	sb.WriteString(" " + String.ToString(entity.BaseEntity.CreateTime) + ")")
			//}

			sb.WriteString(" (?,")
			values = append(values, entity.TaskId)

			sb.WriteString("?,")
			values = append(values, entity.RecordBatch)

			sb.WriteString("?,")
			values = append(values, entity.DeviceId)

			sb.WriteString("?,")
			values = append(values, entity.ObjectName)

			sb.WriteString("?,")
			values = append(values, Json.BuildJson(entity.ObjectValue))

			if pageId < len(pageList)-1 {
				sb.WriteString("?),")
				values = append(values, entity.BaseEntity.CreateTime)
			} else {
				sb.WriteString("?),")
				values = append(values, entity.BaseEntity.CreateTime)
			}
		}

		sql := sb.String()
		if sql[len(sql)-1:] == "," {
			sql = sql[:len(sql)-1] + ";"
		}
		commSql.Client.Exec(sql, values)

		pageList = make([]*edgeEntity.PeriodRecordEntity, 0)
	}
}
